#include "PCM_sourceGlue.h"
#include "ProjectStateGlue.h"

#include "glue.h"
#include "GlueTypes.h"

#include "luna.h"

#include <assert.h>

// BridgePCM_source class, which wraps a PCM_source* 
// and is associated with a {luna} as {self}:

// Its methods have the bridge class as {self} on top of stack.
// The bridge class will keeps a pointer to the C++ PCM_source

// The pushPCM_source() method makes the Bridge object,
// associates its {self} table and its PCM_source C++ object

class BridgePCM_source;

mkchelpers(PCM_source);

class BridgePCM_source : public Bridger
{
public:
	BridgePCM_source(lua_State *L) : Bridger(L)	{}

// now the f-unctions which are exposed to Lua (sounds like mooning the moon that is somewhat clever and almost amusing isn't it!)

// takes :self returns reaper.PCM_source

#define mkfpsovoidtopso(FNAME)	\
int f##FNAME (lua_State *L) {		\
	PCM_source *psp = (PCM_source * ) toReaperObject(L, 1);	\
	PCM_source *src = psp->##FNAME ();		\
	pushPCM_source(L, src);					\
	return 1;	}

mkfpsovoidtopso(Duplicate);
mkfpsovoidtopso(GetSource);

// takes :self returns bool
int fIsAvailable(lua_State *L)
{
	PCM_source *psp = (PCM_source * ) toReaperObject(L, 1);
	bool avail = psp->IsAvailable();
	lua_pushboolean(L, avail);
	return 1;
}

// takes :self, bool returns void

#define mkfpsobooltovoid(FNAME)										\
	int f##FNAME (lua_State *L) {									\
		PCM_source *psp = (PCM_source * ) toReaperObject(L, 1);		\
		bool arg1 = luaL_checkboolean(L, 2);						\
		psp-> FNAME (arg1);											\
		return 0; }	

mkfpsobooltovoid(SetAvailable);
mkfpsobooltovoid(Peaks_Clear);

// takes :self returns string

#define mkfpsotostring(FNAME)	\
	int f##FNAME (lua_State *L) {									\
		PCM_source *psp = (PCM_source * ) toReaperObject(L, 1);		\
		const char *str = psp-> FNAME ();							\
		lua_pushstring(L, str);										\
		return 1;	}

mkfpsotostring(GetType);
mkfpsotostring(GetFileName);

// takes :self, string returns bool
int fSetFileName(lua_State *L)
{
	PCM_source *psp = (PCM_source * ) toReaperObject(L, 1);
	const char *name = luaL_checkstring(L, 2);
	bool supported = psp->SetFileName(name);
	lua_pushboolean(L, supported);
	return 1;
}

// takes :self, reaper.PCM_source returns void
int fSetSource(lua_State *L)
{
	PCM_source *psp = (PCM_source * ) toReaperObject(L, 1);
	PCM_source *src = (PCM_source * ) toReaperObject(L, 2);
	psp->SetSource(src);
	return 0;
}
// takes :self returns int

#define mkfpsotoint(FNAME)	\
	int f##FNAME (lua_State *L) {		\
		PCM_source *psp = (PCM_source * ) toReaperObject(L, 1);			\
		int answer = psp-> FNAME ();					\
		lua_pushnumber(L, answer);						\
		return 1; }

mkfpsotoint(GetNumChannels);
mkfpsotoint(GetBitsPerSample);
mkfpsotoint(PeaksBuild_Begin);
mkfpsotoint(PeaksBuild_Run);

// takes :self returns double

#define mkfpsotodouble(FNAME)	\
	int f##FNAME (lua_State *L) {										\
		PCM_source *psp = (PCM_source * ) toReaperObject(L, 1);			\
		double answer = psp-> FNAME ();									\
		lua_pushnumber(L, answer);										\
		return 1; }

mkfpsotodouble(GetSampleRate);
mkfpsotodouble(GetLength);
mkfpsotodouble(GetLengthBeats);
mkfpsotodouble(GetPreferredPosition);

// takes :self, LUD(HWND) returns int
int fPropertiesWindow(lua_State *L)
{
	PCM_source *psp = (PCM_source * ) toReaperObject(L, 1);
	HWND hw = (HWND) lua_touserdata(L, 2);
	int answer = psp->PropertiesWindow(hw);
	lua_pushnumber(L, answer);
	return 1;
}

// takes :self returns void
int fPeaksBuild_Finish(lua_State *L)
{
	PCM_source *psp = (PCM_source * ) toReaperObject(L, 1);
	psp->PeaksBuild_Finish();
	return 0;
}
// takes :self returns void
// but does something so syntactically different from
// normal self->void that it gets its own function:

int fDelete(lua_State *L)
{
	PCM_source *psp = (PCM_source * ) toReaperObject(L, 1);
	delete psp;
	return 0;
}

int fGetSamples(lua_State *L)
{
	PCM_source_transfer_t *block = 
		(PCM_source_transfer_t *) calloc(1, sizeof(PCM_source_transfer_t) );

	PCM_source *psp = (PCM_source * ) toReaperObject(L, 1);

	block->time_s = luaL_checknumber(L, 2);
	block->samplerate = luaL_checknumber(L, 3);
	block->nch = luaL_checkinteger(L, 4);
	block->length = luaL_checkinteger(L, 5);

	block->samples = (double *) lua_newuserdata(L, block->nch * block->length * sizeof(double));
		//(double *) malloc( block->length * sizeof(double));

	psp->GetSamples(block);

	lua_pushinteger(L, block->samples_out);

	free(block);

	return 2;	// samples pushed by newuserdata, then samples_out
}

//    virtual void GetPeakInfo(PCM_source_peaktransfer_t *block)=0;

int fGetPeakInfo(lua_State *L)
{
	PCM_source_peaktransfer_t *block = 
		(PCM_source_peaktransfer_t *) calloc(1, sizeof(PCM_source_peaktransfer_t) );

	PCM_source *psp = (PCM_source * ) toReaperObject(L, 1);

	block->start_time = luaL_checknumber(L, 2);	// start time of block
	block->peakrate = luaL_checknumber(L, 3);	// end time of block (waahh?  huhhh?)
	block->numpeak_points = luaL_checkinteger(L, 4); // desired number of points for data
	block->nchpeaks = luaL_checkinteger(L, 5); // number of channels of peaks data requested

	block->peaks = (double *) lua_newuserdata(L, block->nchpeaks * block->numpeak_points * sizeof(double));
		//(double *) malloc( block->length * sizeof(double));

	psp->GetPeakInfo(block);

	lua_pushinteger(L, block->peaks_out);

	free(block);

	return 2;	// peaks pushed by newuserdata, then peaks_out
}
#if 0
typedef struct
{
  double start_time; // start time of block
  double peakrate;   // end time of block

  int numpeak_points; // desired number of points for data

  int nchpeaks; // number of channels of peaks data requested

  double *peaks;  // peaks output (caller allocated)
  int peaks_out; // number of points actually output (less than desired means at end)

  int output_mode; // 0=peaks, 1=waveform, 2=midi

  double absolute_time_s;

  double *peaks_minvals; // can be NULL, otherwise receives minimum values
  int peaks_minvals_used;

  int *exp[32]; // future use
} PCM_source_peaktransfer_t;

#endif
// 	virtual int Extended(int call, void *parm1, void *parm2, void *parm3) { return 0; } // return 0 if unsupported
// takes :self, int call, lud(void *), lud(void *), lud(void *)
// returns int
int fExtended(lua_State *L)
{
	PCM_source *psp = (PCM_source * ) toReaperObject(L, 1);
	int call = luaL_checkinteger(L, 2);
	void *parm1 = checkLud(L, 3);
	void *parm2 = checkLud(L, 4);
	void *parm3 = checkLud(L, 5);

	int answer = psp->Extended(call, parm1, parm2, parm3);

	lua_pushinteger(L, answer);
	return 1;
}

// we may break all of the Extended thingies into named funcs

int fAddMidiEvents(lua_State *L)
{
	PCM_source *psp = (PCM_source * ) toReaperObject(L, 1);

	midi_realtime_write_struct_t *mrp = 
		(midi_realtime_write_struct_t *) lua_touserdata(L, 2);

	int answer = psp->Extended(PCM_SOURCE_EXT_ADDMIDIEVENTS,
					mrp, NULL, NULL);

	lua_pushnumber(L, answer);
	return 1;
}

//    virtual void SaveState(ProjectStateContext *ctx)=0;
// takes (:self, reaper.ProjectStateContext (for now at least) )
// returns void
int fSaveState(lua_State *L)
{
	PCM_source *psp = (PCM_source * ) toReaperObject(L, 1);
	ProjectStateContext *ctx = toProjectStateContext(L, 2);
	psp->SaveState(ctx);
	return 0;
}

//    virtual int LoadState(char *firstline, ProjectStateContext *ctx)=0; // -1 on error
// takes (:self, reaper.ProjectStateContext, string) returns int

int fLoadState(lua_State *L)
{
	PCM_source *psp = (PCM_source * ) toReaperObject(L, 1);
	ProjectStateContext *ctx = toProjectStateContext(L, 2);
	char *firstline = (char *) lua_tostring(L, 3);

	int answer = psp->LoadState(firstline, ctx);

	lua_pushinteger(L, answer);
	return 1;
}

// luna wrapper interface:
public:
	static const char className[];
    static const Luna<BridgePCM_source>::RegType Register[];
};

#define psoreg(NAME) \
{	#NAME , &BridgePCM_source::f##NAME }

const char BridgePCM_source::className[] = "ReaperPCM_source";
const Luna<BridgePCM_source>::RegType BridgePCM_source::Register[] = {
	psoreg(Duplicate),
	psoreg(GetSource),
	psoreg(IsAvailable),
	psoreg(SetAvailable),
	psoreg(Peaks_Clear),
	psoreg(GetType),
	psoreg(GetFileName),
	psoreg(SetFileName),
	psoreg(SetSource),
	psoreg(GetNumChannels),
	psoreg(GetBitsPerSample),
	psoreg(PeaksBuild_Begin),
	psoreg(PeaksBuild_Run),
	psoreg(GetSampleRate),
	psoreg(GetLength),
	psoreg(GetLengthBeats),
	psoreg(GetPreferredPosition),
	psoreg(PropertiesWindow),
	psoreg(PeaksBuild_Finish),
	psoreg(Delete),
	psoreg(GetSamples),
	psoreg(GetPeakInfo),

	psoreg(Extended),
	psoreg(AddMidiEvents),

	psoreg(SaveState),
	psoreg(LoadState),

  { 0 }
};
